/*- * Copyright © 2011 Diamond Light Source Ltd. * * This file is part of GDA. * * GDA is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License version 3 as published by the Free * Software Foundation. * * GDA is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with GDA. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.gda.ui.components; import java.util.ArrayList; import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.FigureCanvas; import org.eclipse.draw2d.Graphics; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.MouseEvent; import org.eclipse.draw2d.MouseListener; import org.eclipse.draw2d.MouseMotionListener; import org.eclipse.draw2d.Panel; import org.eclipse.draw2d.PositionConstants; import org.eclipse.draw2d.RectangleFigure; import org.eclipse.draw2d.Shape; import org.eclipse.draw2d.Triangle; import org.eclipse.draw2d.XYLayout; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.swt.SWT; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Composite that has two slider which can be slided towards each other or away - this is used as a histogram control * for the tomography alignment view. */ public class ColourSliderComposite extends Composite { private int maximum = 100000; private FigureCanvas figCanvas; private Panel topSliderHolder; private Triangle topTriangleFigure; private RectangleFigure topClosureFigure; private Panel bottomSliderHolder; private Triangle bottomTriangleFigure; private Shape bottomClosureFigure; private ColorGradientedFigure histogramRect; private Dragger topSliderDragger; private Dragger bottomSliderDragger; private ArrayList<IColourSliderListener> colourSliderListeners; private ColorGradientedFigure upperGradientedFigure; private IFigure lowerGradientedFigure; private final static int OUTER_GRADIENT_WIDTH = 28; private static final Logger logger = LoggerFactory.getLogger(ColourSliderComposite.class); private int bottomLimitInPixel = 0; private int topLimitInPixel; private double maximumLimit; private boolean topSliderMoved = false; private boolean bottomSliderMoved = false; public void addColourSliderListener(IColourSliderListener colourSliderListener) { colourSliderListeners.add(colourSliderListener); } public void removeColourSliderListener(IColourSliderListener colourSliderListener) { colourSliderListeners.remove(colourSliderListener); } public void setMaximum(int maximum) { this.maximum = maximum; } /** * Slider listener which propagates the events to the composites listening to the colour slider. */ public interface IColourSliderListener { void colourSliderRegion(double upperLimit, double lowerLimit); } public ColourSliderComposite(Composite parent, int style) { super(parent, style); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; layout.verticalSpacing = 0; layout.horizontalSpacing = 0; setLayout(layout); Button btnReset = new Button(this, SWT.WRAP); GridData gd = new GridData(); gd.widthHint = 30; gd.heightHint = 90; btnReset.setLayoutData(gd); btnReset.setBackground(ColorConstants.white); btnReset.addPaintListener(new PaintListener() { @Override public void paintControl(PaintEvent e) { GC gc = e.gc; int y = 0; for (char c : "R E S E T".toCharArray()) { gc.drawText(new String(new char[] { c }), 10, y += 7, true); } } }); btnReset.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { bottomSliderMoved = false; topSliderMoved = false; organizeFigures(figCanvas.getContents()); notifyListeners(); } }); figCanvas = new FigureCanvas(this); figCanvas.setContents(getContents()); figCanvas.getViewport().setContentsTracksHeight(true); figCanvas.getViewport().setContentsTracksWidth(true); figCanvas.setLayoutData(new GridData(GridData.FILL_BOTH)); colourSliderListeners = new ArrayList<ColourSliderComposite.IColourSliderListener>(); } public double getCountForPixel(int pixel) { double pixel0 = getPixelLocation(0); double pixelMax = getPixelLocation(maximum); double pixelProportion = maximum / (pixel0 - pixelMax); double countAtPixel = (pixel0 - pixel) * pixelProportion; return countAtPixel; } private IFigure getContents() { RectangleFigure boundaryFigure = new RectangleFigure() { @Override public void paint(Graphics graphics) { super.paint(graphics); graphics.drawLine(0, bottomLimitInPixel, 30, bottomLimitInPixel); } }; boundaryFigure.setLayoutManager(new ColourSliderCompositeLayout()); boundaryFigure.setBackgroundColor(ColorConstants.white); // Top slider figure topSliderHolder = new Panel(); topSliderHolder.setLayoutManager(new XYLayout()); topTriangleFigure = new Triangle(); topTriangleFigure.setDirection(PositionConstants.SOUTH); topTriangleFigure.setFill(true); topTriangleFigure.setBackgroundColor(ColorConstants.buttonDarker); topTriangleFigure.setForegroundColor(ColorConstants.black); topTriangleFigure.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND)); topClosureFigure = new RectangleFigure(); topClosureFigure.setBackgroundColor(ColorConstants.black); topClosureFigure.setForegroundColor(ColorConstants.black); topClosureFigure.setOpaque(true); topClosureFigure.setFill(true); topSliderHolder.add(topTriangleFigure, new Rectangle(0, 0, 20, 15)); topSliderHolder.add(topClosureFigure, new Rectangle(0, 13, 20, 1)); topSliderDragger = new Dragger(); topSliderHolder.addMouseMotionListener(topSliderDragger); topSliderHolder.addMouseListener(topSliderDragger); topSliderHolder.setBounds(new Rectangle(5, 5, 20, 15)); // Bottom slider figure bottomSliderHolder = new Panel(); bottomSliderHolder.setLayoutManager(new XYLayout()); bottomTriangleFigure = new Triangle(); bottomTriangleFigure.setDirection(PositionConstants.NORTH); bottomTriangleFigure.setFill(true); bottomTriangleFigure.setBackgroundColor(ColorConstants.buttonDarker); bottomTriangleFigure.setForegroundColor(ColorConstants.black); bottomTriangleFigure.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND)); bottomClosureFigure = new RectangleFigure(); bottomClosureFigure.setFill(true); bottomClosureFigure.setBackgroundColor(ColorConstants.black); bottomClosureFigure.setForegroundColor(ColorConstants.black); bottomSliderHolder.add(bottomTriangleFigure); bottomSliderHolder.add(bottomClosureFigure); bottomSliderDragger = new Dragger(); bottomSliderHolder.addMouseMotionListener(bottomSliderDragger); bottomSliderHolder.addMouseListener(bottomSliderDragger); bottomSliderHolder.setBounds(new Rectangle(5, 5, 20, 15)); // upperGradientedFigure = new ColorGradientedFigure(ColorConstants.white, ColorConstants.white); // lowerGradientedFigure = new ColorGradientedFigure(ColorConstants.white, ColorConstants.white); histogramRect = new ColorGradientedFigure(ColorConstants.black, ColorConstants.white); boundaryFigure.add(upperGradientedFigure); boundaryFigure.add(lowerGradientedFigure); boundaryFigure.add(histogramRect); boundaryFigure.add(topSliderHolder); boundaryFigure.add(bottomSliderHolder); return boundaryFigure; } private class ColorGradientedFigure extends RectangleFigure { public ColorGradientedFigure(Color color1, Color color2) { this.setBackgroundColor(color1); this.setForegroundColor(color2); } @Override protected void paintClientArea(Graphics graphics) { super.paintClientArea(graphics); graphics.fillGradient(getBounds(), true); } } private class ColourSliderCompositeLayout extends XYLayout { @Override public void layout(IFigure parent) { super.layout(parent); // Parent is the rectangle that holds both the triangles. Rectangle parentBounds = parent.getBounds(); parent.setSize(30, parentBounds.height); organizeFigures(parent); } } class Dragger extends MouseMotionListener.Stub implements MouseListener { private Point last; @Override public void mouseReleased(MouseEvent e) { e.consume(); } @Override public void mouseDoubleClicked(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { last = e.getLocation(); e.consume(); } @Override public void mouseDragged(MouseEvent e) { Point p = e.getLocation(); Dimension delta = p.getDifference(last); Rectangle topSliderBounds = topSliderHolder.getBounds(); Rectangle bottomSliderBounds = bottomSliderHolder.getBounds(); int xStart = topSliderBounds.x; int heightMoved = delta.height; if (e.getSource() == topSliderHolder) { topSliderMoved = true; int movedY = topSliderBounds.y + delta.height; // if (movedY + topSliderBounds.height < getPixelLocation(maximumLimit)) { movedY = topSliderBounds.y; heightMoved = 0; } else if (movedY + topSliderBounds.height > bottomSliderBounds.y) { movedY = topSliderBounds.y; heightMoved = 0; } topSliderHolder.setLocation(new Point(xStart, movedY)); upperGradientedFigure.setBounds(new Rectangle(1, 0, OUTER_GRADIENT_WIDTH, topSliderBounds.y)); notifyListeners(); } else if (e.getSource() == bottomSliderHolder) { // Restrict the bottom slider to go beyond the height of the parent panel bottomSliderMoved = true; int movedY = bottomSliderBounds.y + heightMoved; // if (movedY > getPixelLocation(0)) { movedY = bottomSliderBounds.y; heightMoved = 0; } else if (movedY < topSliderBounds.y + topSliderBounds.height) { movedY = bottomSliderBounds.y; heightMoved = 0; } bottomSliderHolder.setLocation(new Point(xStart, movedY)); lowerGradientedFigure.setBounds(new Rectangle(1, bottomSliderBounds.y + bottomSliderBounds.height, OUTER_GRADIENT_WIDTH, ColourSliderComposite.this.getSize().y)); notifyListeners(); } int histogramHeight = bottomSliderBounds.y - (topSliderBounds.y + topSliderBounds.height); histogramRect.setBounds(new Rectangle(1, topSliderBounds.y + topSliderBounds.height, OUTER_GRADIENT_WIDTH, histogramHeight)); last = p; } } public double getLowerValue() { Rectangle bottomSliderBounds = bottomSliderHolder.getBounds(); return getCountForPixel(bottomSliderBounds.y); } public double getUpperValue() { Rectangle topSliderBounds = topSliderHolder.getBounds(); return getCountForPixel(topSliderBounds.y + topSliderBounds.height); } public static void main(String[] args) { final Display display = new Display(); final Shell shell = new Shell(display, SWT.SHELL_TRIM); shell.setBounds(new org.eclipse.swt.graphics.Rectangle(0, 0, 100, 800)); shell.setLayout(new GridLayout()); shell.setBackground(ColorConstants.black); ColourSliderComposite sliderComposite = new ColourSliderComposite(shell, SWT.DOWN); shell.setText(sliderComposite.getClass().getName()); sliderComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); sliderComposite.setMaximum(65534); sliderComposite.setMaximumLimit(65534); IColourSliderListener lis = new IColourSliderListener() { @Override public void colourSliderRegion(double upperLimit, double lowerLimit) { System.out.println("Upper Limit:" + upperLimit); System.out.println("Lower Limit:" + lowerLimit); } }; sliderComposite.addColourSliderListener(lis); shell.open(); sliderComposite.moveBottomSliderTo(10000); // Thread.sleep(7000); // sliderComposite.moveTopSliderTo(50000); // // Thread.sleep(5000); // sliderComposite.moveTopSliderTo(65534); // // Thread.sleep(3000); // sliderComposite.moveTopSliderTo(80000); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } display.dispose(); } public int getPixelLocation(double count) { if (bottomLimitInPixel != 0) { int totalNumberPixels = bottomLimitInPixel - topLimitInPixel; // logger.debug("totalNumberPixels:{}", totalNumberPixels); int totalCount = maximum; double countsPerPixel = totalCount / totalNumberPixels; // logger.debug("countsPerPixel:{}", countsPerPixel); double pixelAtCount = count / countsPerPixel; // logger.debug("pixelAtCount:{}", pixelAtCount); double countLocInPixel = bottomLimitInPixel - pixelAtCount; // logger.debug("countLocInPixel:{}", countLocInPixel); return (int) countLocInPixel; } return 0; } public void setMaximumLimit(double maximumLimit) { this.maximumLimit = maximumLimit; } @Override public void dispose() { topSliderHolder.removeMouseListener(topSliderDragger); topSliderHolder.removeMouseMotionListener(topSliderDragger); bottomSliderHolder.removeMouseListener(bottomSliderDragger); bottomSliderHolder.removeMouseMotionListener(bottomSliderDragger); colourSliderListeners.clear(); super.dispose(); } private void notifyListeners() { for (final IColourSliderListener lis : colourSliderListeners) { logger.debug("Upper value:{}", getUpperValue()); logger.debug("Lower value:{}", getLowerValue()); new Thread(new Runnable() { @Override public void run() { lis.colourSliderRegion(getUpperValue(), getLowerValue()); } }).start(); } } private void organizeFigures(IFigure parent) { // Parent is the rectangle that holds both the triangles. Rectangle parentBounds = parent.getBounds(); topLimitInPixel = 40; bottomLimitInPixel = parentBounds.height - 40; // Rectangle topSliderHolderBounds = topSliderHolder.getBounds(); int xStart = topSliderHolderBounds.x; if (!topSliderMoved) { int maxLimitPixelLoc = getPixelLocation(maximumLimit); int topSliderStart = maxLimitPixelLoc; if (maxLimitPixelLoc != 0) { topSliderStart = maxLimitPixelLoc - topSliderHolderBounds.height; } // logger.debug("topSliderStart:{}", topSliderStart); topSliderHolder.setLocation(new Point(xStart, topSliderStart)); topTriangleFigure.setLocation(new Point(5, topSliderHolderBounds.y - 1)); topClosureFigure.setLocation(new Point(5, (topSliderHolderBounds.y + topSliderHolderBounds.height) - 3)); } /**/ if (!bottomSliderMoved) { Rectangle bottomSliderHolderBounds = bottomSliderHolder.getBounds(); bottomSliderHolder.setLocation(new Point(xStart, 0)); if (bottomSliderHolderBounds.y < topSliderHolderBounds.y + 25) { bottomSliderHolderBounds.setLocation(5, parentBounds.height - 40); } bottomTriangleFigure.setBounds(new Rectangle(5, bottomSliderHolderBounds.y, 20, 15)); bottomClosureFigure.setBounds(new Rectangle(5, bottomSliderHolderBounds.y, 20, 1)); } // repaintBorderGradient(parentBounds); // } private void repaintBorderGradient(Rectangle parentBounds) { Rectangle topSliderHolderBounds = topSliderHolder.getBounds(); int histogramHeight = bottomSliderHolder.getBounds().y - (topSliderHolder.getBounds().y + topSliderHolder.getBounds().height); histogramRect.setBounds(new Rectangle(1, topSliderHolder.getBounds().y + topSliderHolder.getBounds().height, OUTER_GRADIENT_WIDTH, histogramHeight)); // int upperGradientHeight = topSliderHolderBounds.y; upperGradientedFigure.setBounds(new Rectangle(1, 5, OUTER_GRADIENT_WIDTH, upperGradientHeight - 5)); // int lowerGradientY = bottomSliderHolder.getLocation().y + bottomSliderHolder.getSize().height; int lowerGradientHeight = parentBounds.height - lowerGradientY - 5; lowerGradientedFigure.setBounds(new Rectangle(1, lowerGradientY, OUTER_GRADIENT_WIDTH, lowerGradientHeight)); } public void moveTopSliderTo(int value) { if (value > maximumLimit) { value = (int)maximumLimit; } if (bottomLimitInPixel - topLimitInPixel > 0) { topSliderMoved = true; double valPerPixel = ((double) bottomLimitInPixel - topLimitInPixel) / maximum; logger.debug("Value per pixel:{}", valPerPixel); logger.debug("Value :{}", value); Rectangle topSliderHolderBounds = topSliderHolder.getBounds(); int topSliderYPos = (int) (bottomLimitInPixel - (value * valPerPixel)); int xStart = topSliderHolderBounds.x; topSliderHolder.setLocation(new Point(xStart, topSliderYPos)); logger.debug("Top slider Y new position:{}", topSliderYPos); topTriangleFigure.setLocation(new Point(5, topSliderHolderBounds.y - 1)); topClosureFigure.setLocation(new Point(5, (topSliderHolder.getLocation().y + topSliderHolderBounds.height) - 3)); repaintBorderGradient(figCanvas.getContents().getBounds()); } } public void moveBottomSliderTo(int value) { if (value < 0) { value = 0; } if (bottomLimitInPixel - topLimitInPixel > 0) { bottomSliderMoved = true; double valPerPixel = ((double) bottomLimitInPixel - topLimitInPixel) / maximum; logger.debug("Value per pixel:{}", valPerPixel); logger.debug("Value :{}", value); Rectangle bottomSliderHolderBounds = bottomSliderHolder.getBounds(); int bottomSliderYPos = (int) (bottomLimitInPixel - (value * valPerPixel)); int xStart = bottomSliderHolderBounds.x; bottomSliderHolder.setLocation(new Point(xStart, bottomSliderYPos)); logger.debug("Top slider Y new position:{}", bottomSliderYPos); bottomTriangleFigure.setLocation(new Point(5, bottomSliderHolderBounds.y - 1)); bottomClosureFigure.setLocation(new Point(5, (topSliderHolder.getLocation().y + bottomSliderHolderBounds.height) - 3)); repaintBorderGradient(figCanvas.getContents().getBounds()); } } }